home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Game-Power
/
Amiga Game-Power.iso
/
anwendungen
/
gw print
/
bcpl
/
bcpl.doc
< prev
next >
Wrap
Text File
|
1994-05-20
|
11KB
|
207 lines
BCPL
Author: Bill Kinnersley
Date: Mar 12, 1988
Mail: Physics Dept.
Montana State University
Bozeman, MT 59717
BITNET: iphwk@mtsunix1
INTERNET: iphwk%mtsunix1.bitnet@cunyvm.cuny.edu
UUCP: ...psuvax1!mtsunix1.bitnet!iphwk
TRIPOS
AmigaDOS is a port of the TRIPOS operating system developed at
Cambridge University. From the beginning, Unix and C were meant for
each other. The same thing goes for BCPL and TRIPOS.
On the one hand, TRIPOS was written in a high level language
(BCPL) to provide easy portability from one hardware to another. A
TRIPOS port requires only the construction of a native kernel to provide
process management and message passing--exactly the services handled on
the Amiga by Exec. On the other hand, BCPL requires extensive run-time
support that TRIPOS is designed to offer.
In fact the coupling between system and application is even
stronger in TRIPOS than in Unix. A TRIPOS application program is like a
subroutine of the operating system. It requires shared access to system
variables (the Global Vector) and resident system libraries. As a result,
BCPL programs tend to be smaller than C programs.
TRIPOS was never designed to run on mainframes, nor on single
user micros. From the beginning it was intended to run a network of
workstations. The target was a collection of small memory, small disk
capacity machines in a network environment. Multitasking and message
passing are clearly an essential feature of such an environment.
Presumably the hooks for networking are still present in AmigaDOS...
BCPL
BCPL itself is an ancestor of C and is actually a somewhat lower
level language. (A sample BCPL program is given in the ROM KernelManual,
Appendix G.) References to BCPL and TRIPOS are:
Richards, Martin, "TRIPOS--A Portable Operating System
for Mini-computers",
Software Practice and Experience, 9, 513-526 (1979)
Richards, Martin, "BCPL--the language and its compiler"
Cambridge University Press, 1979
ISBN 0-521-21965-5 QA76.73 B17 R5
Emery, Glyn, "BCPL and C", Blackwell, 1986
ISBN 0-632-01607-8 QA76.73 B38 E44
Moody, Ken, "A Coroutine Mechanism for BCPL",
Software Practice and Experience, 10, 765 (1980)
BPTRS
BCPL is a typeless language--every data item takes up the
same amount of storage. In the 68000 family, pointers are 32 bits.
Hence Amiga TRIPOS is forced to store all data in 32 bit longwords.
Unfortunately, 68000 memory is not longword addressable. Assembly
expressions like 10(A0,D1) calculate an effective address assuming
that the offsets 10 and D1 are measured in bytes. This leads to the
need for a distinction between APTRs which are byte addresses, and
BPTRs which are longword addresses. BPTR = APTR/4, and BPTRs can only
point to locations which are longword aligned.
(Strings are the only exception to the uniform size rule. BCPL
strings are packed, four characters to a longword.)
MULTITASKING
The main AmigaDOS facility for multitasking is the CLI. CLI's
are kept in a fixed array, so there is a maximum number of CLI's allowed
at any time. Every BCPL program must be launched from its own CLI.
When a CLI executes a command, it loads the program with LoadSeg
and calls it as a subroutine (not a coroutine as claimed by the AmigaDOS
manual). It does not create a separate process. Thus the command
inherits all of the CLI's existing environment: the Task, the Process,
the Input and Output file handles, etc. When the program returns, the
CLI is there to take care of the cleanup chores. CLI's may be created
in two ways:
NewCLI creates an interactive CLI. A new window is created,
along with a new instance of the CON: device. The default Input and
Output file handles refer to this device. Interactive CLI's are
destroyed by EndCLI merely by pushing them into the background. A
background CLI with nothing to do automatically commits suicide.
RUN creates a CLI in the background that executes a given
command and then terminates. The Input handle is a fake one containing
the command in its input buffer. The Output handle is shared with
that of the creator.
An AmigaDOS Process may spawn an unlimited number of children
using CreateProc. (This is what WorkBench does.) A process created in
this manner automatically starts execution at once, but typically its
first few lines of code causes it wait at the Process DOSPort for a
startup message. It then continues execution and replies to the message
when done. This approach is necessary because someone else needs to do
the unloading: either the parent, the WorkBench, or a CLI.
LoadWB creates a child process, WorkBench, but exits without
waiting for a reply. As a result WorkBench cannot terminate.
INITIAL ENVIRONMENT
Upon entry to any BCPL routine, the register contents are as
follows:
d0 - amount of global area currently in use
d1-d4 - up to 4 parameters. Further parameters can be passed on
the stack (see below).
d5-d7 - unused
a0 - base of system address space - always 0.
RAM addresses are computed as offsets from a0
a1 - base of the current BCPL stack frame
a2 - pointer to the BCPL Global Vector
a3 - return address of the caller
a4 - entry address
a5 - pointer to a "caller" service routine
a6 - pointer to a "returner" service routine
a7 - stack for temporaries
This register environment is available to any application
program. However if you're programming in C, the startup code supplied
by your linker generally ignores the initial register contents and
eventually they get overwritten.
The parameters passed by the CLI to an application are:
d0 - length of command line
a0 - APTR to command line
The command itself may be found in the CLI->cli_Command field. Two items
are available on the stack: pointers to the top and the bottom of the
stack that was allocated.
STACKS AND CALLS
The a7 stack is rarely used by AmigaDOS, except for interfacing
calls to Exec which must be C-like. Normal BCPL calls use the a1 stack.
It is organized by frames of local variables, growing toward higher RAM
addresses.
In C the caller pushes parameters on the stack in reverse order,
then does the call. The callee pushes its own locals on the stack as
needed and restores the original stack pointer when done. The caller
then pops the parameters off. More specifically, C uses the 68000 LINK
and UNLK instructions to maintain a stack pointer (SP) and a frame
pointer (FP). Upon entry to a subroutine:
SP--->Nth local
...
1st local
FP--->old FP
return address
2nd param
...
In BCPL, parameters and locals are not pushed. There is a frame
pointer, a1, but no stack pointer. Instead the compiler maintains a
"current size". The caller puts his parameters in d1-d4, his current
size in d0, the subroutine address in a4, and then jsr's to (a5).
The (a5) entry routine increments a1 by d0. It pops the return
address off the a7 stack into a3, saves a1, a3, and a4 in locations just
BELOW a1, saves d1-d4 just ABOVE a1 (all without changing a1), and then
jumps to (a4). Upon entry to a subroutine:
old a1
return address
entry address
a1--->1st param
2nd param
...
(Note that BCPL frames grow toward higher memory instead of lower.) The
(a6) exit routine restores a1, a3, and a4, and jumps to (a3).
A BCPL routine must therefore preserve a0, a1, a2, a5, a6 and a7.
Results are typically returned in d1 and/or a1.
For example, suppose your last global is at 100(a1). You must
call a subroutine with d0 = 110. The (a5) routine will save registers
in 104, 108, and 10c, and put the first four parameters at 110, 114, 118,
and 11c. The called routine (using the new a1) will find them at (a1),
4(a1), 8(a1), and c(a1). If you want to pass a 5th parameter, you must
put it ahead of time at 120(a1). The called routine will find it at 10(a1).
BCPL variables are either global or local. Global variables are
located in the Global Vector and are visible to all modules. They are
referenced by an offset from a2.
In C, the linker combines the globals declared by various program
modules and arranges them in the common area. BCPL is not linked! The
BCPL programmer must handle these details himself, declaring explicitly
where each global is to be located in the Global Vector.
Blocks may be nested, but locals declared in surrounding blocks
are not visible, i.e. nonlocal variables may not be referenced.
SEGMENTS
A program exists on the disk in sections called hunks. When the
program is loaded, the hunks are separately relocated as segments, and
these are linked together with headers to form a SegList. Process
creation combines the program's SegList with several system SegLists to
make up a SegArray. Execution begins at the beginning of the first
SegList, which is system startup code. Entries in the SegArray are
allowed to be missing (0), and that is probably the reason for using
an array: to allow easy addition and deletion of chunks of code.
For example, if a program is launched from the CLI, the SegArray
looks like (4/sys1/sys2/sys3/0/prog). If the program is RUN, theSegArray
will be (4/sys1/sys2/0/sys4/prog). If the program is CreateProc'ed, the
array is only (2/sys1/sys2).
MODULE STRUCTURE
A BCPL module has the following structure:
dc.l size ;module size in longwords
dc.w 0 ;pad
dc.b '09' ;version
dc.b name ;name of the module as a BSTR
Any number of BCPL subroutines
...
Table of Global definitions
dc.l maxGV ;GV size the module will need.
Each subroutine may be preceded by a BSTR label. The main entry
is always labeled "start ". If the subroutine uses local constants such
as strings, these immediately follow the code.
The definition table is used to place public entry points into
the GV. This is done by the startup code, specifically the library
function installseg(). The table consists of pairs:
(offset into the module in bytes, offset into the GV in longwords).
The table runs in reverse order, and is terminated with a 0. For example,
if the module ends with
dc.l 0, 1,24, 85,504, 88
it means there are two entries to be installed: offset 24 at GV:1 and
offset 504 at GV:85. The last number, 88, is the GV size requested.
All BCPL programs that can be called from the command line are
composed of two hunks. The first hunk is common startup code that makes
a private copy of the system GV before installing the program's globals.
The purpose is to insure that AmigaDOS commands are reentrant. Global
variables installed by one instance will not overwrite those of another.